package recorder

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type Scene struct {
	mu         sync.Mutex
	id         string
	priority   int
	userSubSir string
	outType    string
	timeout    time.Duration
	sources    map[string]*Source
	sourceList []*SourceMap
	expireTime time.Time
	ctx        string
	share      bool // 是否在屏幕共享期间。true：是；false：否
}

type SourceMap struct {
	sourceMap map[string]*Source
}

func NewScene(priority int, subDir string, outType string, timeout time.Duration, ctx string) *Scene {
	scene := &Scene{
		priority:   priority,
		userSubSir: subDir,
		outType:    outType,
		timeout:    timeout,
		ctx:        ctx,
		sources:    make(map[string]*Source),
		sourceList: make([]*SourceMap, 0),
		expireTime: time.Now().Add(timeout),
		share:      false,
	}
	return scene
}

func (s *Scene) SetId(id string) {
	s.mu.Lock()
	s.id = id
	s.mu.Unlock()
}

func (s *Scene) GetOrCreateSource(sourceName string) *Source {
	s.mu.Lock()
	if source, ok := s.sources[sourceName]; ok {
		s.mu.Unlock()
		return source
	}
	source := NewSource(s.id, sourceName)
	s.sources[sourceName] = source
	s.mu.Unlock()
	return source
}

func (s *Scene) GetOrCreateSource_2(currentTime time.Time, sourceName string, acType int) *Source {
	s.mu.Lock()
	defer s.mu.Unlock()
	log.Infof("GetOrCreateSource_2 source name is %s, acType = %d", sourceName, acType)

	source := NewSource(s.id, sourceName)

	if !checkSharedDesktop(sourceName) {
		s.sources[sourceName] = source
	}

	if s.share {
		return source
	}
	if checkSharedDesktop(sourceName) {
		s.share = true
	}
	newSourceMap := &SourceMap{
		sourceMap: make(map[string]*Source),
	}
	if acType != 1 { // 手动开始录制，不创建新的sourceMap,参会人共用一个sourceMap。
		if len(s.sourceList) > 0 {
			for k, v := range s.sourceList[len(s.sourceList)-1].sourceMap {
				v.segment.stopTime = currentTime
				if !s.share {
					newSource := &Source{}
					newSource.id = v.id
					newSource.idleStartTime = v.idleStartTime
					newSource.scene = v.scene
					newSource.nextSegmentSeq = v.nextSegmentSeq
					newSource.segment = &Segment{
						id:        v.segment.id,
						scene:     v.segment.scene,
						source:    v.segment.source,
						tlvFile:   v.segment.tlvFile,
						audio:     v.segment.audio,
						video:     v.segment.video,
						startTime: currentTime,
						stopTime:  time.Time{},
						checker:   v.segment.checker,
					}
					newSourceMap.sourceMap[k] = newSource
				}
			}
		}
	}
	newSourceMap.sourceMap[sourceName] = source
	s.sourceList = append(s.sourceList, newSourceMap)
	return source
}

// 手动结束录制
func (s *Scene) EndRec(currenttime time.Time) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if len(s.sourceList) > 0 {
		lastMap := s.sourceList[len(s.sourceList)-1]
		for _, v := range lastMap.sourceMap {
			v.segment.stopTime = currenttime
		}
	}
}

func (s *Scene) CreateAndCopySource_2(currenttime time.Time, sourceName string) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if !checkSharedDesktop(sourceName) {
		delete(s.sources, sourceName)
	}

	if checkSharedDesktop(sourceName) { // 结束屏幕共享
		// 1.修改标记；
		// 2.结算屏幕共享，不生成新的source;
		// 3.拉取s.source中的数据，放到sourceMap中，追加到sourceList

		s.share = false
		lastSources := s.sourceList[len(s.sourceList)-1]
		lastSources.sourceMap[sourceName].segment.stopTime = currenttime

		if len(s.sources) > 0 {
			sm := &SourceMap{
				sourceMap: make(map[string]*Source),
			}
			for k, v := range s.sources {
				if v != nil {
					source := &Source{
						id:    v.id,
						scene: v.scene,
						segment: &Segment{
							id:        v.segment.id,
							scene:     v.segment.scene,
							source:    v.segment.source,
							tlvFile:   v.segment.tlvFile,
							audio:     v.segment.audio,
							video:     v.segment.video,
							startTime: currenttime,
							stopTime:  time.Time{},
							checker:   v.segment.checker,
						},
						nextSegmentSeq: v.nextSegmentSeq,
						idleStartTime:  v.idleStartTime,
					}
					sm.sourceMap[k] = source
				}
				log.Infof("CreateAndCopySource_2 source name is %s, s.sources starttime is %s", k, s.sources[k].segment.startTime)
			}
			s.sourceList = append(s.sourceList, sm)
			s.sources = make(map[string]*Source)
		}

		return
	}
	if s.share {
		return
	}
	newSourceMap := &SourceMap{
		sourceMap: make(map[string]*Source),
	}

	if len(s.sourceList) > 0 {
		lastMap := s.sourceList[len(s.sourceList)-1]
		for k, v := range lastMap.sourceMap {
			newSource := &Source{}
			v.segment.stopTime = time.Now()
			if k == sourceName {
				continue
			}
			newSource.id = v.id
			newSource.idleStartTime = v.idleStartTime
			newSource.scene = v.scene
			newSource.nextSegmentSeq = v.nextSegmentSeq
			newSource.segment = &Segment{
				id:        v.segment.id,
				scene:     v.segment.scene,
				source:    v.segment.source,
				tlvFile:   v.segment.tlvFile,
				audio:     v.segment.audio,
				video:     v.segment.video,
				startTime: currenttime,
				stopTime:  currenttime,
				checker:   v.segment.checker,
			}
			newSourceMap.sourceMap[k] = newSource
		}
	}
	if len(newSourceMap.sourceMap) > 0 {
		s.sourceList = append(s.sourceList, newSourceMap)
	}

}

// 判断是否共享桌面
func checkSharedDesktop(sourceName string) bool {
	flag := false
	strArr := strings.Split(sourceName, ":")
	types := strArr[len(strArr)-1]
	if types == "desktop" {
		flag = true
	}
	return flag
}

func (s *Scene) GetSource(sourceName string) *Source {
	s.mu.Lock()
	source := s.sources[sourceName]
	s.mu.Unlock()
	return source
}

func (s *Scene) IsLive() bool {
	s.mu.Lock()

	for sourceId, source := range s.sources {
		if source.IsLive() {
			log.Tracef("check scene '%s' source '%s' live, skipped other scenes", s.id, sourceId)
			s.expireTime = time.Time{}
			s.mu.Unlock()
			return true
		}

		//idle := source.IdleTime()
		//if idle < s.timeout {
		//	log.Infof("check scene '%s' source '%s' idle", s.id, sourceId)
		//	live = true
		//	break
		//}

		log.Tracef("check scene '%s' source '%s' not live", s.id, sourceId)
	}

	// 到达这里，说明没有live的source，开始计算超时
	if s.expireTime.IsZero() {
		s.expireTime = time.Now().Add(s.timeout)
	}

	if s.expireTime.After(time.Now()) {
		log.Tracef("scene '%s' will timeout at '%v'", s.id, s.expireTime)
		s.mu.Unlock()
		return true
	}

	s.mu.Unlock()
	return false
}

func (s *Scene) SetCtx(ctx string) {
	s.mu.Lock()
	s.ctx = ctx
	s.mu.Unlock()
}

func (s *Scene) CreateJobFile(outDir string) error {
	s.mu.Lock()

	log.Infof("create job file at '%s'", outDir)

	now := time.Now()
	date := now.Format("20060102")

	tmpFileName := filepath.Join(outDir, date, s.userSubSir, fmt.Sprintf("scene-%s.tmp", s.id))
	parentDir := filepath.Dir(tmpFileName)
	if err := os.MkdirAll(parentDir, os.ModePerm); err != nil {
		log.Errorf("could not create parent dir '%s' to create job for scene '%s': %s", parentDir, s.id, err)
		s.mu.Unlock()
		return err
	}

	tmpFile, err := os.Create(tmpFileName)
	if err != nil {
		log.Errorf("could not create file '%s' to create job for scene '%s': %s", tmpFileName, s.id, err)
		s.mu.Unlock()
		return err
	}

	job := ToJob_2(s)

	encoder := json.NewEncoder(tmpFile)
	encoder.SetIndent("", "\t")

	if err := encoder.Encode(job); err != nil {
		log.Errorf("could not encode scene '%s' in json: %s", s.id, err)
		s.mu.Unlock()
		return err
	}

	finalFileName := filepath.Join(filepath.Dir(tmpFileName), fmt.Sprintf("scene-%s.job", s.id))
	if err := os.Rename(tmpFileName, finalFileName); err != nil {
		log.Errorf("could not rename job file to '%s' for scene '%s'", finalFileName, s.id)
		s.mu.Unlock()
		return err
	}

	log.Infof("created job file '%s' for scene '%s'", finalFileName, s.id)

	s.mu.Unlock()
	return nil
}

func ToJob(s *Scene) *Job {
	now := time.Now()
	date := now.Format("20060102")
	time := now.Format("150405")

	job := &Job{
		Scene:    s.id,
		Date:     date,
		Time:     time,
		Type:     s.outType,
		Priority: s.priority,
		Ctx:      s.ctx,
		SubDir:   s.userSubSir,
		Sources:  make([]*JobSource, 0),
	}

	for _, source := range s.sources {
		job.Sources = append(job.Sources, source.ToJob())
	}

	return job
}

func ToJob_2(s *Scene) *Job_2 {
	now := time.Now()
	date := now.Format("20060102")
	time := now.Format("150405")

	job := &Job_2{
		Scene:      s.id,
		Date:       date,
		Time:       time,
		Type:       s.outType,
		Priority:   s.priority,
		Ctx:        s.ctx,
		SubDir:     s.userSubSir,
		SourceList: make([]*SourcesMgr, 0),
	}
	for _, source := range s.sourceList {
		sourcelist := &SourcesMgr{
			Sources: make([]*JobSource_2, 0),
		}
		for k, v := range source.sourceMap {
			if v.segment.startTime == v.segment.stopTime {
				continue
			}
			log.Infof("the segment is:%s the time is:%s", k, v.segment.startTime)

			sourcelist.Sources = append(sourcelist.Sources, v.ToJob_2())
		}
		job.SourceList = append(job.SourceList, sourcelist)
	}
	return job
}
